import re
import random
from info.models import User
from info.utils.captcha.captcha import captcha
from info.modules.passport import passport_bp
from flask import request, abort, make_response,jsonify,current_app,session
from info import redis_store
from info import constants
from info.response_code import RET
from info.lib.yuntongxun.sms import CCP
from datetime import datetime
from info import db




@passport_bp.route("/sms_code", methods=["POST"])
def send_sms_code():
    """发送短信验证码的后端接口"""

    """
    1.获取参数
        1.1 mobile: 手机号码， image_code:用户填写的图片验证码值， image_code_id: UUID编号
    2.校验参数
        2.1 非空判断
        2.2 手机号码格式校验
    3.逻辑处理
        3.1 根据image_code_id编号去redis中获取正确真实的图片验证码值
            3.1.1 真实的图片验证码有值：将值从redis数据库删除 [避免拿着这个值多次判断]
            3.1.2 真实的图片验证码没有值：图片验证码值过期了
        3.2 比对用户填写的图片验证码值 & 正确的图片验证码真实值
        3.3 不相等：返回错误状态码，提示图片验证码填写错误
        TODO: 提前判断手机号码是否注册过，数据库查询 [提高用户体验]
        3.4 发送短信验证码
        3.4.1 生成6位的随机短信验证码值
        3.4.2 调用CCP类发送短信验证码
        3.4.3 发送短信验证码成功后，保存6位的短信验证码值到redis数据库
    4.返回值
        4.1 发送短信验证码成功
    """
    """发送短信验证码的后端接口"""


    # 1.获取参数
    # 1.1 mobile: 手机号码， image_code:用户填写的图片验证码值， image_code_id: UUID编号
    param_dict = request.json
    mobile = param_dict.get("mobile")
    image_code = param_dict.get("image_code")
    image_code_id = param_dict.get("image_code_id")
    # print(mobile)
    # print("第二次 %s" % image_code)
    # print("第二次 %s" % image_code_id)
    # 2.校验参数
    #   2.1 非空判断

    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
    #   2.2 手机号码格式校验
    if not re.match(r"1[3456789][0-9]{9}",mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式错误")
    # 3.逻辑处理
    #     3.1 根据image_code_id编号去redis中获取正确真实的图片验证码值
    try:
        real_image_code = redis_store.get("imageCode_%s" % image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询图片验证码真实值数据异常")
        #     3.1.1 真实的图片验证码有值：将值从redis数据库删除 [避免拿着这个值多次判断]

    if real_image_code:
        redis_store.delete("imageCode_%s" % image_code_id)
     #     3.1.2 真实的图片验证码没有值：图片验证码值过期了
    else:
        return jsonify(errno=RET.NODATA, errmsg="图片验证码值过期了")
#     3.2 比对用户填写的图片验证码值 & 正确的图片验证码真实值
    if real_image_code.lower() != image_code.lower():
    #     3.3 不相等：返回错误状态码，提示图片验证码填写错误
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码填写错误")
    #     TODO: 提前判断手机号码是否注册过，数据库查询 [提高用户体验]
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户对象异常")
    if user:
        return jsonify(errno=RET.DATAEXIST, errmsg="用户手机号码已经注册")

    #     3.4 发送短信验证码
    #     3.4.1 生成6位的随机短信验证码值
    real_sms_code = random.randint(0, 999999)
    # 不足6位前面补零
    real_sms_code = "%06d" % real_sms_code
    #     3.4.2 调用CCP类发送短信验证码
    ccp = CCP()
    try:
        result = ccp.send_template_sms(mobile, [real_sms_code, constants.SMS_CODE_REDIS_EXPIRES / 60], 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="云通信发送短信验证码异常")

    if result == -1:
        return jsonify(errno=RET.THIRDERR, errmsg="云通信发送短信验证码异常")
    #     3.4.3 发送短信验证码成功后，保存6位的短信验证码值到redis数据库
    try:
        redis_store.setex("SMS_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES, real_sms_code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码值异常")
    # 4.返回值
    #     4.1 发送短信验证码成功
    return jsonify(errno=RET.OK, errmsg="发送短信验证码成功")


# //////////////////////////////////////////////////////////////////////
@passport_bp.route("/image_code")
def get_image_code():
    """生成图形验证码的后端接口"""
    """
    1.获取参数
        1.1 code_id: UUID唯一编号 
    2.校验参数
        2.1 非空判断
    3.逻辑处理
        3.1 调用工具类，生成图形验证码图片，图形验证码的真实值
        3.2 以code_id作为key图形验证码的真实值，存储到redis数据库
    4.返回值
        4.1 将图片数据返回
    """
    # 1.获取参数
    code_id = request.args.get("code_id")
    # print("第一次 %s" % code_id)
    # 1.1 code_id: UUID唯一编号

     # 2.校验参数
       # 2.1非空判断
    if not code_id:
        return abort(404)
    # 3.逻辑处理
        # 3.1调用工具类，生成图形验证码图片，图形验证码的真实值
    image_name, real_image_code, image_data = captcha.generate_captcha()
    # 3.2以code_id作为key图形验证码的真实值，存储到redis数据库
    redis_store.setex("imageCode_%s" % code_id, constants.IMAGE_CODE_REDIS_EXPIRES, real_image_code)
    # 4.返回值
    # 4.1将图片数据返回
    response = make_response(image_data)
    response.headers["Content-Type"] = "png/image"

    return response

#========================账号注册===============================
"""
请求方式：POST
    请求地址：127.0.0.1:5000/passport/register
    请求&返回数据格式：JSON
    思路：
    1. 获取参数（json格式）
        1.1 mobile：手机号码， sms_code:短信验证码， password:密码
    2. 参数校验
       # 2.1 非空判断
       # 2.2 手机号码格式校验
    3. 逻辑处理
        3.1 根据key：SMS_18520340803去redis中获取真实的短信验证码
            有值：从redis数据库删除
            没有值：短信验证码过期了
        3.2 拿用户填写的短信验证码和真实的短信验证码对比
            不相等：提示前端
        3.3 相等： 创建用户对象，并给对应属性赋值，保存到数据库
        3.4 注册成功一般表示登录成功，使用session保存用户基本信息
    4. 返回值
        4.1 返回注册成功
"""

#      请求方式：POST
#     请求地址：127.0.0.1:5000/passport/register
@passport_bp.route("/register", methods= ['POST'])
def register():
    #     请求&返回数据格式：JSON
#     思路：
#     1. 获取参数（json格式）
#     1.1mobile：手机号码， sms_code: 短信验证码， password: 密码
    mobile = request.json.get("mobile")
    sms_code = request.json.get("sms_code")
    password = request.json.get("password")
#       2. 参数校验
#    # 2.1 非空判断
    if not ([mobile,sms_code,password]):
        return jsonify(error= RET.DATAERR, errmsg = "参数不足")
#    # 2.2 手机号码格式校验
    if not re.match(r"1[3456789][0-9]{9}",mobile):
        return jsonify(error=RET.PARAMERR, errmsg = "手机号码格式不对")

         # 3. 逻辑处理
#     3.1 根据key：SMS_18520340803去redis中获取真实的短信验证码
    try:
        real_sms_code = redis_store.get("SMS_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据出现异常")
    if real_sms_code:
#         有值：从redis数据库删除
        redis_store.delete("SMS_%s" % mobile)
    else:
 #       没有值：短信验证码过期了
        return jsonify(error=RET.NODATA, errmsg="短信验证码过期")
#     3.2 拿用户填写的短信验证码和真实的短信验证码对比
    if real_sms_code != sms_code:
#        不相等：提示前端
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码填写错误")
    user = User()
    #给属性赋值：触发的是属性的set方法
    # 3.3 相等： 创建用户对象，并给对应属性赋值，保存到数据库
    user.nick_name = mobile
    user.mobile = mobile
    user.password = password
    # 最后一次登录时间
    user.last_login = datetime.now()
    try:
    # 保存回数据库
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户对象异常")

#     3.4 注册成功一般表示登录成功，使用session保存用户基本信息
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile
    session["user_id"] = user.id
#     4. 返回值
    #  4.1 返回注册成功
    return jsonify(errno=RET.OK, errmsg="注册成功")


#=============================账户登录======================================
"""
请求方式：POST
    请求地址：127.0.0.1:5000/passport/login
    请求&响应数据格式：json
    思路：
    1. 获取参数
        1.1 mobile：手机号码  password:密码
    2. 参数校验
        2.1 非空判断
        2.2 手机格式校验
    3. 业务逻辑
        3.0 根据mobile查询当前用户是否存在
        存在：进行密码对比
        不存在： 提示注册
        3.1 password未加密的密码再次加密后进行比对
        不相等： 提示密码填写错误
        相等：记录用户登录信息，修改用户最后一次登录时间
    4. 返回登录成功
    """

# 请求方式：POST
#     请求地址：127.0.0.1:5000/passport/login
@passport_bp.route("/login",methods=["POST"])
def login():
#     请求&响应数据格式：json
#     思路：
#     1. 获取参数
#   1.1 mobile：手机号码  password:密码
    mobile = request.json.get("mobile")
    password = request.json.get("password")
#     2. 参数校验
#      2.1 非空判断
    if not all([mobile, password]):
        return jsonify(error= RET.DATAERR, errmsg = "参数不足")
#      2.2 手机格式校验
    if not re.match(r"1[3456789][0-9]{9}", mobile):
        return jsonify(error=RET.PARAMERR, errmsg = "手机号码格式不对")
    #     3. 业务逻辑
#         3.0 根据mobile查询当前用户是否存在
    user = None
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据出现异常")

    if not user:
#          存在：进行密码对比
        return jsonify(errno=RET.USERERR, errmsg="用户不存在")

#   不相等： 提示密码填写错误
    if not user.check_passowrd(password):
        # 密码填写错误
        return jsonify(errno=RET.DATAERR, errmsg="密码填写错误")
    # User.query.filter(User.password).first()
    try:
        # 相等：记录用户登录信息，修改用户最后一次登录时间
        user.last_login = datetime.now()
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
    # 数据库回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="用户属性异常")
#      # 3.3 登录成功记录用户信息
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile
    session["user_id"] = user.id

#     4. 返回登录成功
    return jsonify(errno=RET.OK, errmsg="登录成功")